#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
#include "task.hpp"
#include "log.hpp"
#include "threadpool.hpp"
#include "daemon.hpp"

extern Log log;
const std::string defaultip = "0.0.0.0";
const int backlog = 10; // 但是一般不要设置的太大

enum
{
    SocketErr = 1,
    BindErr,
    ListenErr,
};
class TcpServer;

class ThreadData
{
public:
    ThreadData(TcpServer *tser, int sockfd, const std::string &ip, const uint16_t &port) : tser_(tser), sockfd_(sockfd), clientip_(ip), clientport_(port)
    {
    }

public:
    int sockfd_;
    std::string clientip_;
    uint16_t clientport_;
    TcpServer *tser_;
};

class TcpServer
{
public:
    TcpServer(const uint16_t &port, const std::string &ip = defaultip) : port_(port), ip_(ip)
    {
    }
    void Init()
    {
        // 1.创建socket
        listensock_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listensock_ < 0)
        {
            log(Fatal, "socket err,errno: %d,errstring: %s", errno, strerror(errno));
            exit(SocketErr);
        }
        log(Info, "socket success,listensock: %d", listensock_);
        // 2.bind IP和端口号
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        inet_aton(ip_.c_str(), &(local.sin_addr));

        if (bind(listensock_, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            log(Fatal, "bind err,errno: %d,errstring: %s", errno, strerror(errno));
            exit(BindErr);
        }
        log(Info, "bind success,listensock: %d", listensock_);
        // 3.开始监听
        // Tcp是面向连接的，服务器一般是比较“被动的”，服务器一直处于一种一直在等待连接到来的状态
        if (listen(listensock_, backlog) < 0)
        {
            log(Fatal, "listen err,errno: %d,errstring: %s", errno, strerror(errno));
            exit(ListenErr);
        }
        log(Info, "listen success,listensock: %d", listensock_);
    }

    static void *Routine(void *args)
    {
        ThreadData *td = static_cast<ThreadData *>(args);
        td->tser_->Service(td->sockfd_, td->clientip_, td->clientport_);
        delete td;
        return nullptr;
    }
    void Run()
    {
        Daemon();                                 // 守护进程化
        signal(SIGPIPE, SIG_IGN);                 // 把SIGPIPE忽略，防止服务器当读端关闭或者文件描述符关闭时收到这个信号导致服务器关闭
        ThreadPool<Task>::GetInstance()->Start(); // version4 启动线程池
        log(Info, "tcpserver is running");
        while (true)
        {
            // 1.获取新连接
            // signal(SIGCHLD,SIG_IGN);//下面多进程版本的进程等待，可以把SIGCHLD信号忽略，就不用写waitpid了
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sockfd = accept(listensock_, (struct sockaddr *)&client, &len); // 这个sockfd是用来发送和接收消息的
            if (sockfd < 0)
            {
                log(Warning, "accept err,errno: %d,errstring: %s", errno, strerror(errno));
                continue;
            }
            uint16_t clientport = client.sin_port;
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));

            // 2.根据新连接来通信
            log(Info, "get a new link..., sockfd: %d, client ip: %s, client port: %d", sockfd, clientip, clientport);

            // version1 单进程版---缺陷：当有多个用户访问时，其他用户会被阻塞
            //  Service(sockfd, clientip, clientport);
            //  close(sockfd);

            // version2 多进程版---缺陷：创建进程耗资源，成本高
            //  pid_t id=fork();
            //  if(id==0)
            //  {
            //      //child
            //      close(listensock_);
            //      if(fork()>0) exit(0);
            //      Service(sockfd, clientip, clientport);//孙子进程，由1号进程领养
            //      close(sockfd);
            //      exit(0);
            //  }
            //  close(sockfd);
            //  pid_t rid=waitpid(id,nullptr,0);

            // version3 多线程版---缺陷：每来一个用户都要为它创建线程，会导致线程有很多，也是不太有利的
            //  ThreadData* td=new ThreadData(this,sockfd,clientip,clientport);
            //  pthread_t tid;
            //  pthread_create(&tid,nullptr,Routine,td);

            // version4 线程池版本---最优
            Task t(sockfd, clientip, clientport);
            // ThreadPool<Task>::GetInstance()->Start();//在最开始启动不是在这
            ThreadPool<Task>::GetInstance()->Push(t);
        }
    }
    void Service(int sockfd, const std::string &clientip, const uint16_t &clientport)
    {
        while (true)
        {
            char buff[4096];
            int n = read(sockfd, buff, sizeof(buff));
            if (n > 0)
            {
                buff[n] = 0;
                std::cout << "client say@ " << buff << std::endl;
                std::string massege = "tcpserver say@ ";
                massege += buff;
                write(sockfd, massege.c_str(), massege.size());
            }
            else if (n == 0)
            {
                log(Info, "%s:%d quit, server close sockfd: %d", clientip.c_str(), clientport, sockfd);
                break;
            }
            else
            {
                log(Warning, "read error, sockfd: %d, client ip: %s, client port: %d", sockfd, clientip.c_str(), clientport);
                break;
            }
        }
    }
    ~TcpServer()
    {
    }

private:
    int listensock_;
    uint16_t port_;
    std::string ip_;
};